/**
 * \file packrat.h
 * \brief Implementation of the packrat parser (with support for direct
 * left recursion).
 **/

#ifndef R_PACKRAT_H
#define R_PACKRAT_H

#include "standard.h"
#include "map.h"
#include "chunk.h"
#include "token.h"

/**
 * \brief A nonterminal in the grammar.
 **/
typedef uint32_t RNonterminal;

/**
 * \brief State in packrat parser.
 **/
typedef uint32_t RState;

/**
 * \brief A packrat parser grammar.
 **/
struct RGrammarT {
  Chunk chunk;
  int size;
};
typedef struct RGrammarT RGrammar;

/**
 * \brief A semantic value of a given grammar match.
 **/
typedef void* RSemantic;

/**
 * \brief A pointer to a recursive descent matching function.
 **/
struct RParsingT;
typedef RSemantic (*RMatcher) (struct RParsingT*, int precedence);

/**
 * \brief An entry in the "grammar".
 **/
struct RGrammarEntryT {
 RNonterminal nonterminal;
 RMatcher matcher;
};
typedef struct RGrammarEntryT RGrammarEntry;

/**
 * \brief One run of the parser.
 **/
struct RParsingT {
  uint32_t idx; /*< Current place in the stream */
  RGrammar* grammar; /*< Associated grammar. */
  RVector tokens; /*< Tokens being parsed. */
  RValue map; /*< Memoization table. */
};
typedef struct RParsingT RParsing;

/**
 * \brief Expect a token of this type. If found, consume it and return its
 * extra pointer.
 * \return true if token consumed (idx increased). Otherwise, return false
 * (idx remains the same).
 **/
bool r_prat_consume(RParsing* parsing, uint32_t type, void** extra);

/**
 * \brief Add a matcher for a nonterminal to the grammar.
 **/
void r_prat_grammar_add(RGrammar* grammar, RNonterminal nonterm,
                         RMatcher matcher);

/**
 * \brief Create an instance of the packrat grammar.
 **/
RGrammar* r_prat_grammar_new(void);

/**
 * \brief Match a single rule at parsing->idx in the stream. If the match
 * fails, rewind the stream.
 **/
RSemantic r_prat_match(RParsing* parsing, RNonterminal nterm,
                           int precedence);

/**
 * \brief Initialize a new parsing run.
 **/
RParsing r_prat_new_parsing(RGrammar* grammar, RVector tokens);

/**
 * \brief Initiate parsing and return a semantic value (most likely the AST)
 * obtained after matching the start symbol at the beggining of the token
 * list.
 **/
RSemantic r_prat_parse(RParsing* parsing, RNonterminal start);

/**
 * \brief Restore the position in the token to saved.
 **/
#define r_prat_restore(parsing,state) ((parsing)->idx = (state))

/**
 * \brief Save the position in the token to current.
 **/
#define r_prat_save(parsing) ((parsing)->idx)


#endif
